package com.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.exception.ApiException;
import com.model.BuildModelInfo;
import com.model.GenConstants;
import com.model.TableColumn;
import com.model.enumerate.JavaTypeEnum;
import com.model.enumerate.QueryTypeEnum;
import com.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.dialect.SpringStandardDialect;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.StringTemplateResolver;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author: cxt
 * @time: 2022/7/20
 * 模板数据处理逻辑
 */
@Service
@Slf4j
public class TemplateService {

    private SpringTemplateEngine engine;

    public TemplateService() {
        this.initTemplateEngine();
    }

    /**
     * 初始化模板
     */
    private void initTemplateEngine() {
        engine = new SpringTemplateEngine();
        SpringStandardDialect dialect = new SpringStandardDialect();
        engine.setDialect(dialect);
        // 文本解析
        StringTemplateResolver resolver = new StringTemplateResolver();
        resolver.setCacheable(true);
        resolver.setTemplateMode(TemplateMode.TEXT);
        // 添加解析器
        engine.addTemplateResolver(resolver);
    }

    /**
     * 生成代码预览
     *
     * @param modelInfo 表信息
     * @return 预览代码
     */
    public Map<String, String> createCodePreview(BuildModelInfo modelInfo) {
        Map<String, String> resultMap = new HashMap<>();

        List<TableColumn> columList = modelInfo.getColumList();
        if (columList.size() == 0) {
            throw new ApiException("表字段信息不能为空");
        }
        String tableName = columList.get(0).getTableName();

        // 文件描述信息
        Map<String, Object> map = BeanUtil.beanToMap(modelInfo);
        map.put("author", modelInfo.getAuthor() != null ? modelInfo.getAuthor() : "cxtAdmin");
        map.put("describe", modelInfo.getDescribe() != null ? modelInfo.getDescribe() : "");
        SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
        map.put("createDate", DateUtil.getStrTime(new Date(), format));

        // 请求url地址
        String modularName = modelInfo.getModularName();
        String smallName = StrUtil.toCamelCase(tableName);
        map.put("address", modularName + "/" + smallName);

        // 类名称相关
        map.put("smallName", smallName);
        map.put("fileName", StrUtil.upperFirst(smallName));

        // 上面公共部分信息，controller、service、mapper由上面信息即可生成
        String controllerResult = templateReplace(map, "Controller");
        resultMap.put("controllerResult", controllerResult);

        String serviceResult = templateReplace(map, "Service");
        resultMap.put("serviceResult", serviceResult);

        String mapperResult = templateReplace(map, "Mapper");
        resultMap.put("mapperResult", mapperResult);

        // model类信息
        boolean deleteSign = false;
        ArrayList<TableColumn> remove = new ArrayList<>();
        TreeSet<String> modelSet = new TreeSet<>(); // 头部导入的包
        List<String> modelColum = new ArrayList<>(); // 生成的屬性字段

        // vo类信息
        TreeSet<String> voSet = new TreeSet<>();
        List<String> voColum = new ArrayList<>();

        // dto类信息
        TreeSet<String> dtoSet = new TreeSet<>();
        List<String> dtoColum = new ArrayList<>();

        // query类信息
        TreeSet<String> querySet = new TreeSet<>();
        List<String> queryColum = new ArrayList<>();

        for (TableColumn column : columList) {
            // 判断是否有创建者，创建时间……四个字段 是否继承 DeletedBaseModel 或者 BaseModel
            if (arraysContains(GenConstants.BASE_MODEL, column.getJavaAttribute())) {
                remove.add(column);
                continue;
            }
            if (arraysContains(GenConstants.DELETE_BASE_MODEL, column.getJavaAttribute())) {
                remove.add(column);
                deleteSign = true;
                continue;
            }

            // 判断导入包信息，设置到set中
            JavaTypeEnum javaType = column.getJavaType();
            if (JavaTypeEnum.Date.equals(javaType)) {
                String date = "import java.util.Date;";
                modelSet.add(date);
                voSet.add(date);
            }
            if (JavaTypeEnum.BigDecimal.equals(javaType)) {
                String big = "import java.math.BigDecimal;";
                modelSet.add(big);
                voSet.add(big);
            }
            if (JavaTypeEnum.List.equals(javaType)) {
                String ls = "import java.util.List;";
                modelSet.add(ls);
                voSet.add(ls);
            }

            String model = "// " + column.getComment() + "\n";
            String api = "@ApiModelProperty(\"" + column.getComment() + "\")\n";
            String vo = api;
            String dto = api;
            String query = "";

            // 判断主键和自增情况
            if (Objects.equals(column.getColumnKey(), "PRI")) {
                model += Objects.equals(column.getExtra(), "auto_increment") ? "    @TableId(type = IdType.AUTO)\n" : "    @TableId(type = IdType.ASSIGN_ID)\n";
            } else {
                if (Objects.equals(column.getIsNullable(), "NO")) {
                    String na = StringUtils.isEmpty(column.getComment()) ? column.getName() : column.getComment();
                    if (arraysContains(GenConstants.BASIC_CLASS, column.getJavaType().toString())) {
                        dto += "    @NotNull(message = \"" + na + "不能为空\")\n";
                        dtoSet.add("import javax.validation.constraints.NotNull;");
                    } else if (Objects.equals(column.getJavaType(), JavaTypeEnum.String)) {
                        dto += "    @NotBlank(message = \"" + na + "不能为空\")\n";
                        dtoSet.add("import javax.validation.constraints.NotBlank;");
                    } else {
                        dto += "    @NotEmpty(message = \"" + na + "不能为空\")\n";
                        dtoSet.add("import javax.validation.constraints.NotEmpty;");
                    }
                }

                if (column.getQuery()) {
                    query = api;
                    QueryTypeEnum queryType = column.getQueryType();
                    querySet.add("import com.basemodel.QueryType;");
                    querySet.add("import com.common.annotation.QueryCondition;");
                    if (queryType == null || queryType == QueryTypeEnum.EQUAL) {
                        query += "    @QueryCondition(type = QueryType.EQUAL)\n";
                    } else {
                        query += "    @QueryCondition(type = QueryType." + queryType + ")\n";
                    }
                }
            }

            // 设置特殊long类型情况
            if (column.getJavaType() == JavaTypeEnum.Long) {
                String js = "import com.fasterxml.jackson.databind.annotation.JsonSerialize;";
                String ts = "import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;";
                modelSet.add(js);
                voSet.add(js);
                modelSet.add(ts);
                voSet.add(ts);
                String jsonFormat = "    @JsonSerialize(using = ToStringSerializer.class)\n";
                model += jsonFormat;
                vo += jsonFormat;
            }

            String currentColum = "    private " + column.getJavaType() + " " + column.getJavaAttribute() + ";\n";
            model += "    @TableField(value =\"" + column.getName() + "\")\n    private " + column.getJavaType() + " " + column.getJavaAttribute() + ";\n";
            vo += currentColum;
            dto += currentColum;

            modelColum.add(model);
            voColum.add(vo);
            dtoColum.add(dto);
            if (!Objects.equals(query, "")) {
                query += currentColum;
                queryColum.add(query);
            }
        }

        // 删除创建者时间等公共字段
        if (remove.size() == 4) {
            List<TableColumn> cList = (List<TableColumn>) map.get("columList");
            cList.removeAll(remove);
            if (deleteSign) {
                map.put("extends", "extends DeletedBaseModel ");
                modelSet.add("import com.basemodel.DeletedBaseModel;");
            } else {
                map.put("extends", "extends BaseModel ");
                modelSet.add("import com.basemodel.BaseModel;");
            }
        } else if (remove.size() != 0) {
            // 字段里面包含了创建人、时间等信息的部分，需要按普通字段添加
            for (TableColumn column : remove) {
                // 判断是否有date
                // 是否包含date
                if (column.getType().contains("date")) {
                    modelSet.add("import java.util.Date;");
                }
                String createInfo = "// " + column.getComment() + "\n   @TableField(value =\"" + column.getName() + "\")\n    private " + column.getJavaType() + " " + column.getJavaAttribute() + ";\n";
                modelColum.add(createInfo);
            }
        }

        map.put("modelColum", modelColum);
        map.put("voColum", voColum);
        map.put("dtoColum", dtoColum);
        map.put("queryColum", queryColum);
        map.put("modelSet", modelSet);
        map.put("voSet", voSet);
        map.put("dtoSet", dtoSet);
        map.put("querySet", querySet);

        String modelResult = templateReplace(map, "Model");
        resultMap.put("modelResult", modelResult);

        // String voResult = templateReplace(map, "VO"); // TODO 没有用到后续考虑是否要用
        // resultMap.put("mapperResult", mapperResult);

        String dtoResult = templateReplace(map, "DTO");
        resultMap.put("dtoResult", dtoResult);

        String queryResult = templateReplace(map, "Query");
        resultMap.put("queryResult", queryResult);

        return resultMap;
    }

    /**
     * 校验数组是否包含指定值
     *
     * @param arr         数组
     * @param targetValue 值
     * @return 是否包含
     */
    public static boolean arraysContains(String[] arr, String targetValue) {
        return Arrays.asList(arr).contains(targetValue);
    }

    /**
     * 模板数据替换
     */
    public String templateReplace(Map<String, Object> map, String name) {
        String process = "";
        InputStream stream = this.getClass().getResourceAsStream("/temp/" + name + ".Java.vm");
        try {
            if (stream == null) {
                throw new ApiException("读取模板异常");
            }
            // 读取到内容
            String read = IoUtil.readUtf8(stream);
            stream.close();

            Context context = new Context();
            context.setVariables(map);

            process = engine.process(read, context);
            log.info("【{}{}】模板替换成功!", map.get("fileName"), name);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return process;
    }

    /**
     * 保存代码到磁盘
     *
     * @param map       生成的代码
     * @param modelInfo 路径相关信息
     */
    public void saveByDisk(Map<String, String> map, BuildModelInfo modelInfo) {
        log.info("本地保存：{}", modelInfo.getFilePath());
        String replace = modelInfo.getPackageRoute().replace(".", "/");
        String newFilePath = modelInfo.getFilePath() + "/src/main/java/" + replace + "/" + modelInfo.getModularName();

        // 文件名称
        String tableName = StrUtil.toCamelCase(modelInfo.getColumList().get(0).getTableName());

        // 循环存储到本地
        for (Map.Entry<String, String> m : map.entrySet()) {
            String key = m.getKey();
            String value = m.getValue();
            String[] split = key.split("Result");
            try {
                String url = "";
                String fileName = StrUtil.upperFirst(tableName) + StrUtil.upperFirst(split[0]) + ".java";
                // dto query vo 放在service文件夹中
                if ("dto".equals(split[0])) {
                    url = newFilePath + "/service/" + split[0];
                    fileName = StrUtil.upperFirst(tableName) + split[0].toUpperCase() + ".java";
                } else if ("query".equals(split[0])) {
                    url = newFilePath + "/service/" + split[0];
                } else if ("model".equals(split[0])) {
                    url = newFilePath + "/" + split[0];
                    fileName = StrUtil.upperFirst(tableName) + ".java";
                } else {
                    url = newFilePath + "/" + split[0];
                }
                // 确定vo存在时候再放开
                // else if("vo".equals(split[0])){
                // directory = Files.createDirectories(Paths.get(newFilePath + "/" + split[0]));
                // }

                Path directory = Files.createDirectories(Paths.get(url));
                FileOutputStream out = new FileOutputStream(directory.toString() + "/" + fileName);
                out.write(value.getBytes(StandardCharsets.UTF_8));
                out.close();
            } catch (IOException e) {
                throw new ApiException("本地保存失败，文件夹创建异常");
            }
        }
    }
}
